package com.l2qq.common;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaCookieConfig;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.jfinal.SaAnnotationInterceptor;
import cn.dev33.satoken.jfinal.SaTokenActionHandler;
import cn.dev33.satoken.jfinal.SaTokenContextForJfinal;
import cn.dev33.satoken.util.SaTokenConsts;
import com.alibaba.druid.filter.logging.Log4jFilter;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.jfinal.config.*;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect;
import com.jfinal.plugin.cron4j.Cron4jPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.server.undertow.UndertowServer;
import com.jfinal.template.Engine;
import com.l2qq.common.caffeine.CaffeineCache;
import com.l2qq.common.caffeine.CaffeineKit;
import com.l2qq.common.kit.MyDruidFilter;
import com.l2qq.common.kit.TookKit;
import com.l2qq.common.model._MappingKit;
import com.l2qq.common.task.TokenCleanTask;
import com.l2qq.vben.auth.PgSaTokenDao;
import com.l2qq.vben.auth.SaTokenInterceptor;
import com.l2qq.vben.auth.StpInterfaceImpl;

/**
 * 本 demo 仅表达最为粗浅的 jfinal 用法，更为有价值的实用的企业级用法
 * 详见 JFinal 俱乐部: https://jfinal.com/club
 * <p>
 * API 引导式配置
 */
public class AppConfig extends JFinalConfig {
    public static Prop p;

    /**
     * 启动入口，运行此 main 方法可以启动项目，此 main 方法可以放置在任意的 Class 类定义中，不一定要放于此
     */
    public static void main(String[] args) {
        UndertowServer.create(AppConfig.class).addHotSwapClassPrefix("cn.dev33.satoken.jfinal.").start();
        //UndertowServer.start(AppConfig.class);
    }

    /**
     * PropKit.useFirstFound(...) 使用参数中从左到右最先被找到的配置文件
     * 从左到右依次去找配置，找到则立即加载并立即返回，后续配置将被忽略
     */
    static void loadConfig() {
        if (p == null) {
            p = PropKit.useFirstFound("config.txt", "config-dev.txt");
        }
    }

    public static DruidPlugin createDruidPlugin() {
        loadConfig();

        return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password"));
    }

    /**
     * 配置常量
     */
    public void configConstant(Constants me) {
        loadConfig();

        me.setDevMode(p.getBoolean("devMode", false));
        /**
         * 支持 Controller、Interceptor、Validator 之中使用 @Inject 注入业务层，并且自动实现 AOP
         * 注入动作支持任意深度并自动处理循环注入
         */
        me.setInjectDependency(true);

        // 配置对超类中的属性进行注入
        me.setInjectSuperClass(true);

        // 直接在 AppConfig 中配置 Sa-Token
        //注册权限验证功能，由saToken处理请求上下文
        SaTokenContext saTokenContext = new SaTokenContextForJfinal();
        SaManager.setSaTokenContext(saTokenContext);
        //加载权限角色设置数据接口
        SaManager.setStpInterface(new StpInterfaceImpl());
        //设置token生成类型
        SaTokenConfig saTokenConfig = new SaTokenConfig();
        saTokenConfig.setTokenStyle(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID);
        saTokenConfig.setTimeout(3 * 24 * 60 * 60); // 3天有效期
        //saTokenConfig.setActiveTimeout(3 * 24 * 60 * 60); //3天无操作就冻结 token
        saTokenConfig.setTokenName("satoken");    //更改satoken的cookies名称
        saTokenConfig.setIsConcurrent(true);        // 允许并发登录
        saTokenConfig.setIsShare(true);             // 共享Cookie
        // 可选：根据需求添加其他配置
        // saTokenConfig.setTokenStyle("uuid");     // Token风格
        saTokenConfig.setActiveTimeout(-1);    // 无操作时Token有效期（-1表示不限制）

        SaCookieConfig saCookieConfig = new SaCookieConfig();
        saCookieConfig.setHttpOnly(true);   //开启cookies 的httponly属性

        saTokenConfig.setCookie(saCookieConfig);
        //satoken持久话整合
        SaManager.setConfig(saTokenConfig);

    }

    /**
     * 配置路由
     */
    public void configRoute(Routes me) {
        // 使用 jfinal 4.9.03 新增的路由扫描功能
        me.scan("com.l2qq.");
    }

    public void configEngine(Engine me) {
        // 添加共享对象
    }

    /**
     * 配置插件
     */
    public void configPlugin(Plugins me) {
        // 配置 druid 数据库连接池插件
        DruidPlugin druidPlugin = new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password"));
        // 配置log插件
        Log4jFilter logFilter = new Log4jFilter();
        logFilter.setStatementLogEnabled(false);
        logFilter.setStatementLogErrorEnabled(true);
        logFilter.setStatementExecutableSqlLogEnable(true);
        druidPlugin.setInitialSize(5); // 初始连接数
        druidPlugin.setMinIdle(5);     // 最小空闲连接
        druidPlugin.setMaxActive(200);  // 最大活跃连接数
        druidPlugin.setMaxWait(30000); // 获取连接超时时间(毫秒)

        druidPlugin.addFilter(logFilter);
        druidPlugin.setValidationQuery("SELECT 1");
        druidPlugin.setTestOnBorrow(false);      // 从连接池获取时验证
        druidPlugin.setTestWhileIdle(true);
        druidPlugin.setTestOnReturn(false);

        // 必须配合的时效参数
        druidPlugin.setTimeBetweenEvictionRunsMillis(60000); // 60秒检查一次
        druidPlugin.setMinEvictableIdleTimeMillis(1800000);   // 30分钟以上空闲才回收
        druidPlugin.addFilter(new MyDruidFilter());
        me.add(druidPlugin);

        // 配置ActiveRecord插件
        ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
        arp.getEngine().addSharedMethod(new TookKit());
        arp.getEngine().addSharedObject("StrKit",new StrKit());
        arp.setDialect(new PostgreSqlDialect());
        arp.setShowSql(false);    // 是否输出 sql 到控制台
        arp.setCache(new CaffeineCache());
        arp.addSqlTemplate("/com/l2qq/sql/_all_sqls.sql");
        // 所有映射在 MappingKit 中自动化搞定
        _MappingKit.mapping(arp);
        me.add(arp);

        // 在配置中添加（每天凌晨执行）
        Cron4jPlugin cron = new Cron4jPlugin();
        cron.addTask("59 23 * * *", new TokenCleanTask());
        me.add(cron);
    }

    /**
     * 配置全局拦截器
     */
    public void configInterceptor(Interceptors me) {
        // Sa-Token 拦截器放在第一位
        me.addGlobalActionInterceptor(new SaTokenInterceptor());
        //开启注解方式权限验证
        me.add(new SaAnnotationInterceptor());

    }

    /**
     * 配置处理器
     */
    public void configHandler(Handlers me) {
//将上下文交给satoken处理
        me.setActionHandler(new SaTokenActionHandler());
       /* me.add(new Handler() {
            //所有请求响应均自动添加 CSP 头
            @Override
            public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {

                // 跳过静态资源（扩展更多路径）
                if (target.startsWith("/static/") || target.endsWith(".css")|| target.startsWith("/upload/")) {
                    next.handle(target, request, response, isHandled);
                    return;
                }
                // 设置 CSP 响应头
                response.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' 'nonce-randomKey'");
                //根据业务需求细化策略，例如允许特定 CDN：
                //"default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'"
                // 包装请求
                request = new XssRequestWrapper(request);
                next.handle(target, request, response, isHandled);
            }
        });*/

    }

    public void onStop() {
        // 1. 持久化 Caffeine 缓存
        CaffeineKit.persistAll();
        // token 持久化数据到数据库
        // 确保数据持久化
        if (SaManager.getSaTokenDao() instanceof PgSaTokenDao) {
            SaManager.getSaTokenDao().destroy();
        }
        System.out.println("onStop ");
    }

    public void onStart() {
        // 1. 先设置持久化路径 设置缓存持久化路径 (使用Web根目录下的data/cache目录)
        String cachePath = PathKit.getWebRootPath() + "/data/cache/";
        CaffeineKit.setPersistenceBasePath(cachePath);
        // 2. 创建特殊缓存实例（在加载数据前创建）
        CaffeineKit.putCache("searchSpecificationRowCache", Caffeine.newBuilder().maximumSize(10_000).build());

        // 3. 最后加载持久化数据（会自动填充已创建的缓存）
        CaffeineKit.loadAllFromDisk();

        // 1.44 版本直接设置全局 DAO
       /* MemorySaTokenDao dao = new MemorySaTokenDao();
        dao.init(); // 加载数据并启动清理线程
        SaManager.setSaTokenDao(dao);*/

        SaManager.setSaTokenDao(new PgSaTokenDao());


        // 添加 JVM 关闭钩子（安全防护）
/*        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            if (SaManager.getSaTokenDao() != null) {
                try {
                    SaManager.getSaTokenDao().destroy();
                } catch (Exception e) {
                    System.err.println("Error during SaToken shutdown: " + e.getMessage());
                }
            }
        }));*/

        // 使用异步执行缓存预热
        /*if (!CommonKit.getRunTimeTest()) {
            CompletableFuture.runAsync(() -> {
                try {
                    CommonKit.buildCacheList();
                } catch (Exception e) {
                    // 异常处理（添加日志记录）
                    e.printStackTrace();
                }
            });
        }*/
    }
}
